﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO.Packaging;
using System.IO;
using System.Xml;
using System.Drawing;
using System.Drawing.Imaging;
using ReporterApplication.Expressions;
using System.Text.RegularExpressions;

namespace ReporterApplication
{
    public sealed class ReportEngine
    {
        public ReportSettings Settings;
        List<ImagePart> ImageParts = new List<ImagePart>();
        int ImagesCount;

        Package package;

        public static StringBuilder LogBuilder = new StringBuilder();

        public void Report(ReportSettings settings)
        {
            LogBuilder.Length = 0;
            
            XmlCIF.Progress = 10;
            XmlCIF.ProgressStep = "Started";

            Settings = settings;

            XmlCIF cif = XmlCIF.Load(Path.Combine(Settings.CifFolder,"cif.xml"));

            File.Copy(settings.TemplateFile, settings.ResultFile, true);

            ReportEngine.LogBuilder.AppendLine("File copied");

            using (package = Package.Open(settings.ResultFile, FileMode.Open))
            {
                ReportEngine.LogBuilder.AppendLine("Package : " + package.PackageProperties.Title);
                
                var replaceList = new List<PackagePart>();
                replaceList = package.GetParts().ToList();

                //set progress by parts count
                //set from 10 % to 80 as big steps == 70% for progress
                var progressStep = 70 / replaceList.Count();
                var currentStep = 0;

                foreach (var part in replaceList)
                {
                    var cesta = part.Uri.ToString();

                   

                    using (var stream1 = part.GetStream())
                    {
                        if (cesta.EndsWith(".xml"))
                        {
                            ReportEngine.LogBuilder.AppendLine("Part : " + cesta);

                            XmlReader sr = XmlReader.Create(stream1);
                            XDocument part_xd = XDocument.Load(sr);

                            sr.Close();
                            stream1.Close();

                            var content = part_xd.Root.Value;

                            if (word.findCommand.IsMatch(content))
                            {
                                ReportEngine.LogBuilder.AppendLine("Trace All start");

//#if(RELEASE)
                                try
                                {
//#endif

                                    TraceAll(part_xd.Root, cif, cif.Notebook);
//#if(RELEASE)
                                }
                                catch (Exception e)
                                {
                                    ReportEngine.LogBuilder.AppendLine("Problem : ");

                                    Exception tempex = e;

                                    while (tempex != null)
                                    {
                                        ReportEngine.LogBuilder.AppendLine(tempex.Message);
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine(tempex.Source);
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine(tempex.TargetSite.ToString());
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine(tempex.StackTrace.ToString());
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine("*********************************");
                                        ReportEngine.LogBuilder.AppendLine("*********************************");

                                        tempex = tempex.InnerException;
                                    }

                                    ReportEngine.LogBuilder.AppendLine("Problem : ");
                                }
//#endif

                                    ReportEngine.LogBuilder.AppendLine("Trace All end");


                                if (cesta.EndsWith("document.xml"))
                                {
                                    int ee = 12;

                                   // ReportEngine.LogBuilder.AppendLine("xml : " + part_xd.Root.Value.ToString());
                                }

                                //delete part data
                                //part.

                                //write result changes
                                var stream = part.GetStream();

                                stream.SetLength(0);

                                TextWriter sw = new StreamWriter(stream);
                                part_xd.Save(sw);

                               

                                sw.Close();
                                stream.Close();
                            }
                        }
                    }

                    //set progress
                    currentStep++;
                    XmlCIF.Progress = currentStep * progressStep + 10; //10 is from previous steps
                }

                //var ip = AddImagePart("D:\\ProvisionReports\\Result\\jpgs\\210558.jpg");
            }

            XmlCIF.Progress = 100;
            XmlCIF.ProgressStep = "Done";
        }

        ImagePart AddImagePart(string fullPath)
        {
            var existingPart = ImageParts.Where(x => x.FullPath == fullPath).SingleOrDefault();

            if (existingPart != null)
                return existingPart;
            
            //create new image part
            ImagePart ip = new ImagePart();

            FileInfo fi = new FileInfo(fullPath);
            string fileName = fi.Name.Substring(0,fi.Name.Length - fi.Extension.Length);

            Uri partUriDocument = PackUriHelper.CreatePartUri(
                             new Uri("/word/document.xml", UriKind.Relative));

            Uri partUriImage = PackUriHelper.CreatePartUri(
                         new Uri("/word/media/" + fileName + ".jpeg", UriKind.Relative));

            PackagePart packagePartResource = package.CreatePart(partUriImage,
                       System.Net.Mime.MediaTypeNames.Image.Jpeg);

            var jpegfile = fullPath.Replace("\\images\\", "\\jpgs\\");
            jpegfile = jpegfile.Replace(".bmp", ".jpg");

            //convert to jpeg
            using (Image img = Image.FromFile(fullPath))
            {
                //img.Save.Save("foo.jpg", ImageFormat.Jpeg);
                img.Save(packagePartResource.GetStream(), ImageFormat.Jpeg);
                

                //set image sizes
                ip.X = img.Size.Width;
                ip.Y = img.Size.Height;
            }

            //create reference

            var packagePartDocument = package.GetPart(partUriDocument);

            var relation = packagePartDocument.CreateRelationship(
                            partUriImage,
                            TargetMode.Internal,
                            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
            
            ip.ID = relation.Id;
            ip.FullPath = fullPath;
            ip.Uri = partUriImage.ToString();

            ImageParts.Add(ip);

            ImagesCount++;
            XmlCIF.ProgressStep = "Image number " + ImagesCount + " imported";

            return ip;
        }

        int tempCounter;

        void startfile(XElement sel, string prefix)
        {
            tempCounter++;
            
            if (sel.Document != null)
                sel.Document.Save("D:\\ProvisionReports\\Result\\temp\\" + tempCounter + prefix + ".xml");
            else
                sel.Save("D:\\ProvisionReports\\Result\\temp\\" + tempCounter + "poskodene" + ".xml");
        }

        void endfile(XElement sel, string prefix)
        {
            tempCounter++;

            if(sel.Document != null)
                sel.Document.Save("D:\\ProvisionReports\\Result\\temp\\" + tempCounter + prefix + ".xml");
            else
                sel.Save("D:\\ProvisionReports\\Result\\temp\\" + tempCounter + "poskodene" + ".xml");
        }


        private void TraceAll(XElement xel, XmlCIF cif, XElement current)
        {
            //test only
            //export temporary xml file

           
            

            if (xel.Name == XName.Get("p", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"))
            {
                //startfile(xel, "abefore");

                string eltext = xel.Value;

                while (IsCommand(eltext))
                {
                    var command = GetCommand(eltext);

                    if (word.ParameterCommands.Contains(command.CommandName))
                    {
                        if (command.CommandName == "text" && command.Exp == "{text$#subject#Trigger}")
                        {
                            int ee = 12;
                        }
                        
                        string AttributeName = command.Parameters;

                        var textres = cif.GetAttributeValue(AttributeName, current);

                        if (textres.Contains("Financial Management can be defined as:"))
                        {
                            int ee = 12;
                        }

                        textres = cif.GetAttributeValue(AttributeName, current);

                        if (!string.IsNullOrEmpty(textres))
                            ReplaceText(xel, command.Exp, textres);
                        else
                            //ReplaceText(xel, command.Exp, "prikaz : " + command.CommandName + " + parametre : " + command.Parameters);
                            ReplaceText(xel, command.Exp, "");
                    }

                    if (command.CommandName == "diagram")
                    {
                        string AttributeName = command.Parameters;

                        var textres = cif.GetAttributeValue(AttributeName, current);
                        if (!string.IsNullOrEmpty(textres))
                        {
                            if (File.Exists(textres))
                            {
                                //add image
                                var ip = AddImagePart(textres);

                                //goto nearest image and replace id
                                var nearestimage = xel;

                                if (xel.Descendants(word.blip).Count() == 0)
                                    nearestimage = xel.ElementsAfterSelf().FirstOrDefault(x => x.Descendants(word.blip).Count() > 0);

                                if (nearestimage == null && xel.Parent != null)
                                {
                                    ReportEngine.LogBuilder.AppendLine("nearest image is missing in document");

                                    ReportEngine.LogBuilder.AppendLine(command.Parameters);
                                    //ReportEngine.LogBuilder.AppendLine(textres);
                                }

                                XElement imageBlip = null;

                                if (nearestimage != null)
                                {
                                    imageBlip = nearestimage
                                         .Descendants(word.blip).FirstOrDefault();
                                }


                                if (imageBlip != null)
                                {
                                    imageBlip.Attribute(word.attribute_embed).Value = ip.ID;

                                    //set ratio for image
                                    //get graph object
                                    var sizesEl = nearestimage
                                    .Descendants()
                                    .Where(x => x.Attribute("cx") != null).First();

                                    var cx = int.Parse(sizesEl.Attribute("cx").Value);
                                    var cy = int.Parse(sizesEl.Attribute("cy").Value);

     //                               imageWidthEMU =
     //                                     (long)(
     //                                     (imageFile.Width / imageFile.HorizontalResolution) * 914400L);
     //                               imageHeightEMU =
     //                                     (long)(
     //                                     (imageFile.Height / imageFile.VerticalResolution) * 914400L);

                                    if ((cx > ip.X * 9525) && (cy*2 > ip.Y * 9525))
                                    {
                                        cx = ip.X*9525;
                                        cy = ip.Y*9525;
                                    }
                                    else
                                    {
                                        if (ip.X > ip.Y && cx > cy)
                                        {
                                            var pomer = (1.0 * ip.Y) / (1.0 * ip.X);
                                            cy = (int)((1.0 * cx) * pomer);
                                        }
                                        else
                                        {
                                            var pomer = (1.0 * ip.X) / (1.0 * ip.Y);
                                            cx = (int)((1.0 * cy) * pomer);
                                        }
                                    }

                                    //update new values
                                    foreach (var cxel in nearestimage
                                    .Descendants()
                                    .Where(x => x.Attribute("cx") != null))
                                    {
                                        cxel.Attribute("cx").Value = cx.ToString();
                                        cxel.Attribute("cy").Value = cy.ToString();
                                    }
                                }
                            }
                            else
                            {
                                //report error missing file
                                ReportEngine.LogBuilder.AppendLine("Error: Missing file image " + textres);
                            }

                            ReplaceText(xel, command.Exp, "");//textres
                        }
                        else
                        {
                            ReplaceText(xel, command.Exp, "");//obrazok
                            
                            //mazat riadok je chyba zistit preco
                            //xel.ElementsAfterSelf().First().Remove();
                            //zistit ako zmazat riadok bez poskodenia suboru

                           // if(xel.Parent != null)
                           // var el = xel.ElementsAfterSelf().First();
                        }

                    }

                   // var rsidR = "";
                   // var rsidR = "";
                   // var rsidRPr = "";

                    if (word.RepeatingCommands.Contains(command.CommandName) )
                    {
                        if(command.CommandName == "list")
                        {
                            int ee = 12;

                            //load all list attributes to reuse
                        }
                        //ReplaceText(xel, command.Exp, "prikaz : " + command.CommandName + " + sekcia : " + command.Parameters);
                        ReplaceText(xel, command.Exp, "");
                    }

                    if (command.CommandName == word.StartSectionCommand && xel.Parent != null)
                    {
                        if (command.Exp.Contains("Business Domains?planningHorizon"))
                        {
                            int eei = 12;
                        }

                        if (command.Exp.Contains("+subject$Requirement Model$members$Requirement"))
                        {
                            int eei = 12;
                        }


                        if (command.Exp.Contains("$childs"))
                        {
                            int eei = 12;
                        }
                        
                        var startSection = xel;
                        var endsection = FindEndSection(xel, command.Parameters);

                        if (endsection == null)
                        {
                            //missing end section 
                            ReportEngine.LogBuilder.AppendLine("Problem : ");
                            ReportEngine.LogBuilder.AppendLine("missing end section : " + command.Exp);
                        }

                        List<XElement> innerEls = new List<XElement>();

                        bool isBetween = true;

                        foreach (var nextEl in xel.ElementsAfterSelf())
                        {
                            if (nextEl == endsection)
                                isBetween = false;
                            
                            if(isBetween)
                                innerEls.Add(nextEl);
                        }

                        //var cifElems = cif.getBySetLabel(command.Parameters);

                        var cifElems = cif.GetElementsByPath(command.Parameters, current);

                        if(cifElems.Count() == 0)
                            cifElems = cif.getBySetLabel(command.Parameters);

                        if (command.FilterExp != null)
                        {
                            var filteredList = new List<XElement>();

                            foreach (var filterElem in cifElems)
                            {
                                var args = new ExpArgs { CIF = filterElem, XmlCif = cif };

                                var isAllowed = command.FilterExp.Eval(args);

                                if (isAllowed)
                                {
                                    filteredList.Add(filterElem);
                                }
                            }

                            cifElems = filteredList;
                        }

                        foreach (var el in cifElems)
                        {
                            //clone elements
                            List<XElement> newList = new List<XElement>();

                            foreach (var newel in innerEls)
                                newList.Add(Clone(newel));

                          
                            xel.AddAfterSelf(newList);

                            //var iteerateList = new List<XElement>(newList);

                            foreach (var trEl in newList)
                            {
                                TraceAll(trEl, cif, el);
                            }
                               
                        }

                        //delete from start to end all
                        startSection.Remove();

                        foreach (var innerel in innerEls)
                        {
                            innerel.Remove();
                        }

                        endsection.Remove();
                    }

                    if (command.CommandName == "row")
                    {
                        if (command.Exp == "{Row$$members$Business Class}")
                        {
                            int ee = 12;
                        }
                        
                        //find closest tr
                        var trEl = ClosestParent(xel, x => x.Name == word.tr);

                        IEnumerable<XElement> innerList;

                        if (command.Parameters.StartsWith("childs"))
                            //innerList = cif.getChildrens(current.Attribute("id").Value, command.Parameters, current);
                            innerList = cif.GetElementsByPath(command.Parameters, current);
                        else
                            //skontrolovat preco bol vrateny iba text a nie element
                            innerList = cif.GetElementsByPath(command.Parameters, current); //innerList = cif.getBySetLabel(command.Parameters);

                        foreach (var el in innerList)
                        {
                            var newEl = Clone(trEl);

                          //  if (newEl.Parent != null)
                            {
                                trEl.AddAfterSelf(newEl);

                                TraceAll(newEl, cif, el);
                            }
                        }

                        trEl.Remove();
                    }

                    if (command.CommandName == "list")
                    {
                        //find closest tr
                        var trEl = ClosestParent(xel, x => x.Name == word.p);

                        //find first "r"

                        var firstrr = trEl.Descendants().Where(x => x.Name.LocalName == "r").First();



                      //  firstrr.Add(new XAttribute(word.rsidR, trEl.Attribute(word.rsidR).Value));
                       // firstrr.Add(new XAttribute( word.rsidRPr, trEl.Attribute(word.rsidP).Value));

                        IEnumerable<XElement> innerList;

                        //xel.PreviousNode()

                        if (command.Parameters.StartsWith("childs"))
                            //innerList = cif.getChildrens(current.Attribute("id").Value, command.Parameters, current);
                            innerList = cif.GetElementsByPath(command.Parameters, current);
                        else
                            //skontrolovat preco bol vrateny iba text a nie element
                            innerList = cif.GetElementsByPath(command.Parameters, current); //innerList = cif.getBySetLabel(command.Parameters);


                       // bool existas = false;
                       // foreach (var el in innerList)
                       // {
                        //    var newEl = Clone(trEl);
                        //    trEl.AddAfterSelf(newEl);

                          //  TraceAll(newEl, cif, el);

                        //    existas = true;
                       // }

                       // if(existas)
                            trEl.Remove();
                    }

                    eltext = xel.Value;
                }

                //endfile(xel, "after");
            }

            
            var iteratelist = xel.Elements().ToList();

            foreach (var cel in iteratelist)
            {
                if (cel.Parent != null)
                {
                    TraceAll(cel, cif, current);

                    //if (xel.Value.Contains("{"))
                    //{
                    //    int ee = 12;
                    //}
                }
            }

            
        }

        static object Clone(XNode node)
        {
            XElement element = node as XElement;
            if (element != null)
            {
                return new XElement(element.Name,
                    element.Attributes(),
                    element.Nodes().Select(n => Clone(n)));
            }
            //if (node is XEntity)
            //    return new XEntity(((XText)node).Value);
            return node;
        }

        static XElement Clone(XElement element)
        {
            return (XElement)Clone((XNode)element);
        }

        private static XElement ClosestParent(XElement xel, Func<XElement, bool> exp)
        {
            var parent = xel;

            while (!exp(parent) && parent != null)
            {
                parent = parent.Parent;
            }

            return parent;
        }

        private static bool IsCommand(string text)
        {
            if (word.findCommand.IsMatch(text))
            {
                return true;
            }

            return false;
        }
        private static Ann GetCommand(string text)
        {
            if (word.findCommand.IsMatch(text))
            {
                var matchResult = word.findCommand.Match(text);
                var command = matchResult.Groups["command"].Value.Trim();
                var parameters = matchResult.Groups["parameters"].Value;
                var filter = matchResult.Groups["filter"].Value;

                if (!string.IsNullOrEmpty(filter))
                    filter = filter.Substring(1, filter.Length - 1);


                if (!string.IsNullOrEmpty(command))
                    command = command.ToLower();

                var resCommand = new Ann
                {
                    CommandName = command,
                    Parameters = parameters,
                    Exp = matchResult.Value,
                    Filter = filter
                };

                if (!string.IsNullOrEmpty(filter))
                {
                    
                    resCommand.FilterExp = getFilterExp(filter);
                }

                return resCommand;
            }

            return Ann.Undefined;
        }



        private XElement FindEndSection(XElement xel, string parameters)
        {
            //filter only over string without space, to prevent user errors
            parameters = parameters.Replace(" ","");

            foreach (var nextEl in xel.ElementsAfterSelf())
            {
                var command = GetCommand(nextEl.Value);
                if (command != Ann.Undefined 
                    && command.CommandName == word.EndSectionCommand
                    && command.Parameters.Replace(" ","") == parameters)
                {
                    return nextEl;
                }
            }

            return null;
        }

        private static void ReplaceText(XElement xel, string exp, string value)
        {
            //format text


            if (value.Contains("\n") && xel.Parent != null)
            {
                string[] textRows = value.Split(new string[] { "\n" }, StringSplitOptions.None);

                var lastel = xel;

                //add start parts before replace string, and put into first row


                foreach (var curRow in textRows)
                {
                    var newxel = Clone(xel);

                    if (lastel == xel)
                    {
                        ReplaceExpOnly(newxel, exp, curRow);

                        lastel.AddAfterSelf(newxel);

                        lastel = newxel;
                    }
                    else
                    {

                        var txel = newxel.Descendants(word.t).Where(x=>x.Value.Contains("{")).First();

                        //check first line and replace only like in next part

                        var removenodes = newxel.Descendants(word.r).Where(x => x.Descendants(word.t).Count() == 0 || x.Descendants(word.t).Where(y => y == txel).Count() == 0).ToList();

                        var dcount = newxel.Descendants(word.r).ToList();

                        foreach (var remnode in removenodes)
                        {
                            remnode.Remove();
                        }

                        //end removing other row with text

                        txel.Value = curRow;

                        lastel.AddAfterSelf(newxel);

                        lastel = newxel;
                    }
                }

                xel.Remove();
            }
            
            //find start position of EXP
            //find endposition of EXP
            ReplaceExpOnly(xel, exp, value);
        }

        private static void ReplaceExpOnly(XElement xel, string exp, string value)
        {
            if (xel.Value.Contains(exp))
            {

                //var startExp = xel.Elements(word.r).Elements(word.t).Where(
                //    x => !string.IsNullOrEmpty(x.Value)
                //        && x.Value.Contains("{")).First();

                var startExp = xel.Descendants(word.t).Where(
                    x => !string.IsNullOrEmpty(x.Value)
                        && x.Value.Contains("{")).First();

                string oldValue = startExp.Value;

                bool hasEndElement = true;

                //remove "{" or whole element
                bool removeStart = false;
                bool removeEnd = false;

                //logic for start element
                if (startExp.Value == "{")
                {
                    //remove element
                    removeStart = true;
                }
                else
                {
                    int pos = startExp.Value.IndexOf("{");
                    int endPos = startExp.Value.IndexOf("}");

                    if (endPos == -1)
                    {
                        //remove start "{"
                        startExp.Value = startExp.Value.Substring(0, pos);

                        value = startExp.Value + value;
                    }
                    else
                    {
                        //remove start "{"  and end "}"
                        //insert value between start stop
                        var beforestring = startExp.Value.Substring(0, pos);
                        var afterstring = startExp.Value.Substring(endPos + 1, startExp.Value.Length - endPos - 1);
                        startExp.Value = beforestring + value + afterstring;

                        hasEndElement = false;
                        //remove element if empty - optional

                        return;
                    }
                }

                if (hasEndElement)
                {
                    var endExp = xel.Elements(word.r).Elements(word.t).Where(
                        x => !string.IsNullOrEmpty(x.Value)
                            && x.Value.Contains("}")).First();

                    if (endExp.Value == "}")
                    {
                        //remove element
                        removeEnd = true;
                    }
                    else
                    {
                        int endPos = endExp.Value.IndexOf("}");
                        var afterstring = endExp.Value.Substring(endPos + 1, endExp.Value.Length - endPos - 1);
                        //set ending string
                        endExp.Value = afterstring;
                    }

                    //remove all between start and end "{" "}"
                    if (endExp != null)
                    {
                        while (startExp.Parent.ElementsAfterSelf().First() != endExp.Parent)
                        {
                            startExp.Parent.ElementsAfterSelf().First().Remove();
                        }
                    }

                    if (removeEnd)
                    {
                        endExp.Parent.Remove();
                    }

                    startExp.Value = value;
                }

            }
        }



        /********************************************************************
         * expressions methods
         * 
         * 
         * 
         */

        static Logical getFilterExp(string filter)
        {
            var sample = filter;// "[name]*='test' || test=='skuska' || 'tiest'*='tie'";

            var startExpRegex = new Regex("((?<exp>^[" + word.expRegexString + "]+))");
            var nextExpRegex = new Regex("(?<log>\\s[(\\&)(\\|)]{2}\\s)((?<exp>[" + word.expRegexString + "]+))");

            var firstMatch = startExpRegex.Match(sample);
            var firstExp = firstMatch.Groups["exp"].Value;

            var startLog = new Or();
            startLog.SetExp(firstExp, getExp(firstExp));

            Logical currentStep = startLog;

            foreach (Match match in nextExpRegex.Matches(sample))
            {
                var log = match.Groups["log"].Value;
                var exp = match.Groups["exp"].Value;

                Logical stepLog;

                if (log.Contains("&&"))
                {
                    stepLog = new And();
                }
                else
                {
                    stepLog = new Or();
                }

                stepLog.SetExp(exp, getExp(exp));

                currentStep.Next = stepLog;

                currentStep = stepLog;
            }

            //var testres = startLog.Eval(new ExpArgs());//for test only

            int ee = 12;

            return currentStep;
        }



        static Func<ExpArgs, bool> getExp(string expression)
        {
            var innerExpRegex = new Regex("(?<left>[" + word.paramRegexString + "]+)((?<exp>[\\=\\*\\>\\<\\~\\!\\^]{2}))(?<right>[" + word.paramRegexString + "]+)");

            var expMatch = innerExpRegex.Match(expression);

            var left = expMatch.Groups["left"].Value;
            var op = expMatch.Groups["exp"].Value;
            var right = expMatch.Groups["right"].Value;


            Func<ExpArgs, string> leftExp = x => GetValue(left, x);
            Func<ExpArgs, string> rightExp = x => GetValue(right, x);

            //define left right side by types / element / value
            Func<ExpArgs, bool> opExp = null;

            switch (op)
            {
                case "==":
                    opExp = (arg) => leftExp(arg) == rightExp(arg);
                    break;

                case "!=":
                    opExp = (arg) => leftExp(arg) != rightExp(arg);
                    break;

                case "^=":
                    opExp = (arg) => leftExp(arg).StartsWith(rightExp(arg));
                    break;
                case "*=":
                    opExp = (arg) => leftExp(arg).Contains(rightExp(arg));
                    break;
                case "~=":
                    opExp = (arg) => leftExp(arg).Contains(" " + rightExp(arg) + " ");
                    break;

                default:
                    break;
            }

            //opExp = (arg) => leftExp(arg).Contains(rightExp(arg));

            //var ll = leftExp.Invoke(new ExpArgs());
            //var rr = rightExp.Invoke(new ExpArgs());
            //var res = opExp(new ExpArgs()); //for test only

            return opExp;
        }

        static string GetValue(string exp, ExpArgs args)
        {
            var oldExp = exp;
            exp = exp.Trim();

            var stringRegex = new Regex("[\\'\\’](?<value>[\\w\\s\\.]+)[\\'\\’]");

            if (stringRegex.IsMatch(exp))
            {
                var match = stringRegex.Match(exp);
                var value = match.Groups["value"].Value;

                return value;
            }

            var attributeRegex = new Regex("^\\w+");

            if (attributeRegex.IsMatch(exp))
            {
                var attributeName = exp;

                var value = args.XmlCif.GetAttributeValue(attributeName, args.CIF);

                

                //if(value == "now")
                //{
                //    int ee = 12;
                //}))

                return value;
            }

            return string.Empty;
        }
        
    }
}
